/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jps.Interpreter;

import java.util.Iterator;
import java.util.List;
import jps.ast.*;
import jps.ast.UnaryExpression;
import jps.datastore.IntegerObject;
import jps.datastore.SBAObject;
import jps.datastore.StringObject;
import jps.envs.ENVSBinder;
import jps.envs.ENVSStackFrame;
import jps.main.Main;
import jps.qres.*;

/**
 *
 * @author calyJa
 */
public class Interpreter implements ASTVisitor {
    
    @Override
    public void visitUnaryExpression(UnaryExpression expression) {
        
    }

    @Override
    public void visitNameExpression(NameExpression expression) {
        Main.qres.push(Main.envs.bind(expression.getName()));
    }

    @Override
    public void visitGroupasExpression(GroupAsExpression expression) {
        //accepting inner expression
        expression.getInnerExpression().accept(this);

        //instantiating DotExpression Bag Result
        BagResult resgroupas = new BagResult();

        //retriving BagResult from QRESStack that came to existence from accepting the inner expression
        AbstractQueryResult qresResult = Main.qres.pop();
        
        //pushing binder as the result
        Main.qres.push(new BinderResult(expression.getName(), qresResult));        
        
    }

    @Override
    public void visitUnionExpression(UnionExpression expression) {
        
    }

    @Override
    public void visitOrExpression(OrExpression expression) {
        
    }

    @Override
    public void visitAndExpression(AndExpression expression) {
        
    }

    @Override
    public void visitDivisionExpression(DivisionExpression expression) {
        
    }

    @Override
    public void visitMultiplyExpression(MultiplicationExpression expression) {
        
    }

    @Override
    public void visitMinusExpression(MinusExpression expression) {
        
    }

    @Override
    public void visitPlusExpression(PlusExpression expression) {
        
    }

    @Override
    public void visitGreaterThanExpression(GreaterThanExpression expression) {
        
    }

    @Override
    public void visitAsExpression(AsExpression expression) {
        
    }

    @Override
    public void visitEqualsExpression(EqualityExpression expression) {
        BagResult leftResultBag = Main.envs.bind(((NameExpression) expression.getLeftExpression()).getName());
        ReferenceResult result = (ReferenceResult) leftResultBag.getElements().iterator().next();

        SBAObject referencedObjet = Main.sbaStore.getObject(result.getOID());
        TerminalExpression rightExpression = (TerminalExpression) expression.getRightExpression();
        if (referencedObjet instanceof StringObject && rightExpression instanceof StringTerminal) {
            Main.qres.push(new BooleanResult((((StringObject) referencedObjet).getValue() == null ? ((StringTerminal) rightExpression).getName() == null : ((StringObject) referencedObjet).getValue().equals(((StringTerminal) rightExpression).getName()))));
        } else if (referencedObjet instanceof IntegerObject && rightExpression instanceof IntegerTerminal) {
            Main.qres.push(new BooleanResult((((IntegerObject) referencedObjet).getValue() == null ? ((IntegerTerminal) rightExpression).getValue() == null : ((IntegerObject) referencedObjet).getValue().equals(((IntegerTerminal) rightExpression).getValue()))));
        }
    }

    @Override
    public void visitWhereExpression(WhereExpression expression) {
        //accepting left expression
        expression.getLeftExpression().accept(this);

        //instantiating DotExpression Bag Result
        BagResult whereres = new BagResult();

        //retriving BagResult from QRESStack that came to existence from accepting the left expression
        AbstractQueryResult qresResult = Main.qres.pop();
        //casting the result to known BagResult
        BagResult leftBag = (BagResult) qresResult;
        //for every element from the left BagResult accept the right expression and add the result to resdot
        for (AbstractQueryResult leftResult : leftBag.getElements()) {
            //retrivng binders for the left result element
            List<ENVSBinder> bindersList = Main.envs.nested(leftResult, Main.sbaStore);
            //pushing a new frame on the ENVS Stack
            Main.envs.push(new ENVSStackFrame(bindersList));
            //accepting the right expression
            expression.getRightExpression().accept(this);
            //poping the temporary frame with bidners for the left result element
            Main.envs.pop();
            //adding the single result(s) to resdot bag
            AbstractQueryResult rightResult = Main.qres.pop();
            if (rightResult instanceof BooleanResult && ((BooleanResult) rightResult).getBool()) {
                whereres.addResult(leftResult);
            }
        }
        Main.qres.push(whereres);
    }

    @Override
    public void visitDotExpression(DotExpression expression) {
        //accepting left expression
        expression.getLeftExpression().accept(this);

        //instantiating DotExpression Bag Result
        BagResult resdot = new BagResult();

        //retriving BagResult from QRESStack that came to existence from accepting the left expression
        AbstractQueryResult qresResult = Main.qres.pop();
        //casting the result to known BagResult
        BagResult leftBag = (BagResult) qresResult;
        //for every element from the left BagResult accept the right expression and add the result to resdot
        for (AbstractQueryResult leftResult : leftBag.getElements()) {
            //retrivng binders for the left result element
            List<ENVSBinder> bindersList = Main.envs.nested(leftResult, Main.sbaStore);
            //pushing a new frame on the ENVS Stack
            Main.envs.push(new ENVSStackFrame(bindersList));
            //accepting the right expression
            expression.getRightExpression().accept(this);
            //poping the temporary frame with bidners for the left result element
            Main.envs.pop();
            //adding the single result(s) to resdot bag
            AbstractQueryResult rightResult = Main.qres.pop();
            if (rightResult instanceof BagResult) {
                for (Iterator<AbstractQueryResult> it = ((BagResult) rightResult).getElements().iterator(); it.hasNext();) {
                    AbstractQueryResult result = it.next();
                    resdot.addResult(result);
                }
            } else {
                resdot.addResult(rightResult);
            }
        }
        Main.qres.push(resdot);
    }    
}
